/* Console example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "cmd_decl.h"
#include "esp_vfs_fat.h"
#include "nvs_flash.h"
#include "cmd_nvs.h"
#include "cmd_i2c.h"
#include "cmd_test.h"
#include "cmd_tuling.h"
#include "cmd_curl.h"

#include "mdns.h"
#include "lwip/api.h"
#include "lwip/err.h"
#include "lwip/netdb.h"

#include "http_server.h"
#include "wifi_manager.h"

static const char * TAG = "shell";

/* 控制台命令历史记录可以存储到文件中并从文件中加载。
 * 最简单的方法是使用FATFS文件系统
 * wear_levelling库。
 */
#if CONFIG_STORE_HISTORY

#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"

static TaskHandle_t task_http_server = NULL;
static TaskHandle_t task_wifi_manager = NULL;

static void initialize_filesystem()
{
    static wl_handle_t wl_handle;
    const esp_vfs_fat_mount_config_t mount_config = {
            .max_files = 4,
            .format_if_mount_failed = true
    };
    esp_err_t err = esp_vfs_fat_spiflash_mount(MOUNT_PATH, "storage", &mount_config, &wl_handle);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "无法挂载FATFS (%s)", esp_err_to_name(err));
        return;
    }
}
#endif // CONFIG_STORE_HISTORY

static void initialize_nvs()
{
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK( nvs_flash_erase() );
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
}

static void initialize_console()
{
    /* 禁用stdin和stdout上的缓冲 */
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    /* 当按下ENTER键时，Minicom，screen，idf_monitor发送CR */
    esp_vfs_dev_uart_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
    /* 将插入符号移动到下一行的开头 '\n' */
    esp_vfs_dev_uart_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);

    /* 配置UART。请注意，使用REF_TICK以保持波特率
     * 在轻度睡眠模式下，APB频率正在变化时正确。
     */
    const uart_config_t uart_config = {
            .baud_rate = CONFIG_CONSOLE_UART_BAUDRATE,
            .data_bits = UART_DATA_8_BITS,
            .parity = UART_PARITY_DISABLE,
            .stop_bits = UART_STOP_BITS_1,
            .use_ref_tick = true
    };
    ESP_ERROR_CHECK( uart_param_config(CONFIG_CONSOLE_UART_NUM, &uart_config) );

    /* 安装UART驱动程序以进行中断驱动的读写操作 */
    ESP_ERROR_CHECK( uart_driver_install(CONFIG_CONSOLE_UART_NUM,
            256, 0, 0, NULL, 0) );

    /* 告诉VFS使用UART驱动程序 */
    esp_vfs_dev_uart_use_driver(CONFIG_CONSOLE_UART_NUM);

    /* 初始化控制台 */
    esp_console_config_t console_config = {
            .max_cmdline_args = 8,
            .max_cmdline_length = 256,
#if CONFIG_LOG_COLORS
            .hint_color = atoi(LOG_COLOR_CYAN)
#endif
    };
    ESP_ERROR_CHECK( esp_console_init(&console_config) );

    /* 配置linenoise行完成库 */
    /* 启用多行编辑。如果未设置，则长命令将在其中滚动
     * 单线。
     */
    linenoiseSetMultiLine(1);

    /* 告诉linenoise在哪里获得命令完成和提示 */
    linenoiseSetCompletionCallback(&esp_console_get_completion);
    linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);

    /* 设置命令历史记录大小 */
    linenoiseHistorySetMaxLen(100);

#if CONFIG_STORE_HISTORY
    /* 从文件系统加载命令历史记录 */
    linenoiseHistoryLoad(HISTORY_PATH);
#endif
}


void app_main()
{
    initialize_nvs();

#if CONFIG_STORE_HISTORY
    initialize_filesystem();
#endif

    //initialise_wifi();
    
    //auto_wifi();
    initialize_console();
    /* 注册命令行 */
    //执行自动连接wifi
    
    esp_console_register_help_command();
    register_system();
    register_wifi();
    register_nvs();
    register_sntp();
    register_i2ctools();
    register_test_task(); 
    register_tuling();
    register_curl();
    
    	/* 启动HTTP Server任务 */
	xTaskCreate(&http_server, "http_server", 2048, NULL, 5, &task_http_server);
	/* 启动wifi管理器任务 */
	xTaskCreate(&wifi_manager, "wifi_manager", 4096, NULL, 4, &task_wifi_manager);
    

    //初始化sntp时间
    //obtain_time();
    /* 提示在每行之前打印。
     * 这可以定制，动态等。
     */
    const char* prompt = LOG_COLOR_I "jarlen@esp32> " LOG_RESET_COLOR;

    printf("\n"
           "这是一个简单的esp-shell项目，用于学习esp32\n"
           "输入“help”以获得全部命令列表。\n"
           "使用“向上/向下”箭头来浏览命令历史。\n"
           "输入命令名时按“TAB”键自动完成。\n");

    /* 弄清楚终端是否支持转义序列 */
    int probe_status = linenoiseProbe();
    if (probe_status) { /* 零表示成功 */
        printf("\n"
               "您的终端应用程序不支持转义序列。\n"
               "线路编辑和历史记录功能已禁用。\n"
               "在Windows上，请尝试使用Putty。\n");
        linenoiseSetDumbMode(1);
#if CONFIG_LOG_COLORS
        /* 由于终端不支持转义序列，不要在提示中使用颜色代码。
         */
        prompt = "jarlen@esp32> ";
#endif //CONFIG_LOG_COLORS
    }

    /* 主循环 */
    while(true) {
        /* 使用linenoise获取一行。
         * 按下ENTER时返回该行。
         */
        vTaskDelay(10 / portTICK_PERIOD_MS);
        char* line = linenoise(prompt);
        if (line == NULL) { /* 忽略空行 */
            continue;
        }
        /* 将命令添加到历史记录中 */
        linenoiseHistoryAdd(line);
#if CONFIG_STORE_HISTORY
        /* 将命令历史保存到文件系统 */
        linenoiseHistorySave(HISTORY_PATH);
#endif

        /* 尝试运行该命令 */
        int ret;
        esp_err_t err = esp_console_run(line, &ret);
        if (err == ESP_ERR_NOT_FOUND) {
            printf("无法识别的命令\n");
        } else if (err == ESP_ERR_INVALID_ARG) {
            // 命令是空的
        } else if (err == ESP_OK && ret != ESP_OK) {
            printf("命令返回非零错误代码: 0x%x (%s)\n", ret, esp_err_to_name(err));
        } else if (err != ESP_OK) {
            printf("内部错误: %s\n", esp_err_to_name(err));
        }
        /* linenoise在堆上分配行缓冲区，因此需要释放它 */
        linenoiseFree(line);
    }
}
